package com.ly.wxPay.config;

import com.ly.wxPay.config.properties.WxPayProperties;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;

/**
 * 微信支付配置类
 * @desc：
 *  支付时间戳验证：超过5分钟，则是无效请求
 *  微信支付的二维码支付有效期为2h，
 *  微信会在支付成功之后，通知商户，48h内重复通知直到商户返回200
 */
@Slf4j
@Data
@Configuration
@RequiredArgsConstructor
public class WxPayConfig {

    private final WxPayProperties wxPayProperties;

    /**
     * 获取商户的私钥文件
     *
     * @param filename
     * @return
     */
    public PrivateKey getPrivateKey(String filename) {
        try {
            InputStream is = new ClassPathResource(filename).getInputStream();
            return PemUtil.loadPrivateKey(is);
        } catch (IOException e) {
            throw new RuntimeException("私钥文件不存在", e);
        }
    }

    /**
     * 获取签名验证器
     * @desc： ScheduledUpdateCertificatesVerifier：定时更新证书，一小时更新一次
     * @return
     */
    @Bean
    public ScheduledUpdateCertificatesVerifier getVerifier() {
        log.info("获取签名验证器");
        // 获取商户私钥
        PrivateKey privateKey = getPrivateKey(wxPayProperties.getPrivateKeyPath());
        // 私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(wxPayProperties.getMchSerialNo(), privateKey);
        // 身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(wxPayProperties.getMchId(), privateKeySigner);
        // 使用定时更新的签名验证器，不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                wechatPay2Credentials,
                wxPayProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        return verifier;
    }

    /**
     * 获取http请求对象
     *
     * @param verifier
     * @return
     */
    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {
        log.info("获取httpClient");
        // 获取商户私钥
        PrivateKey privateKey = getPrivateKey(wxPayProperties.getPrivateKeyPath());
        WechatPayHttpClientBuilder builder =
                WechatPayHttpClientBuilder.create()
                        .withMerchant(wxPayProperties.getMchId(), wxPayProperties.getMchSerialNo(), privateKey)
                        .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }

    /**
     * 获取HttpClient，无需进行应答签名验证，跳过验签的流程
     */
    @Bean(name = "wxPayNoSignClient")
    public CloseableHttpClient getWxPayNoSignClient() {
        // 获取商户私钥
        PrivateKey privateKey = getPrivateKey(wxPayProperties.getPrivateKeyPath());
        // 用于构造HttpClient
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                // 设置商户信息
                .withMerchant(wxPayProperties.getMchId(), wxPayProperties.getMchSerialNo(), privateKey)
                // 无需进行签名验证、通过withValidator((response) -> true)实现
                .withValidator((response) -> true);
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        log.info("== getWxPayNoSignClient END ==");
        return httpClient;
    }
}
